<?php

class WSB_Model
{

	/**
	 * @var array
	 */
	protected $_data;

	/**
	 * @var WSB_Model_Gateway
	 */
	protected $_gateway;

    /**
     * Constructor
     *
     * @param array|object $data
     */
	public function __construct($data = null)
	{
		$this->init();
		if(is_object($data) || is_array($data)) {
			$this->populate($data);
		}
	}

    /**
     * Initialize object
     *
     * Called from {@link __construct()} as FIRST step of object instantiation.
     *
     * @return void
     */
    public function init()
    {
    }

	/**
	 * @param array|object $data
	 */
	public function populate($data)
	{
		if(is_object($data)) {
			$data = (array)$data;
		}
		if(!is_array($data)) {
			$exception = sprintf('Initial data in "%s"::populate() must be an array or object', get_class($this));
        	throw new Exception($exception);
		}
		foreach($data AS $key=>$value) {
			$this->$key = $value;
		}
	}

    /**
     * Set/Get attribute wrapper
     *
     * @param   string $method
     * @param   array $args
     * @return  mixed
     */
    public function __call($method, $args)
    {
        switch (substr($method, 0, 3)) {
            case 'get' :
                $key = $this->_lcfirst(substr($method,3));
                return $this->getData($key);

            case 'set' :
                $key = $this->_lcfirst(substr($method,3));
                $result = $this->setData($key, isset($args[0]) ? $args[0] : null);
                return $result;

            case 'has' :
                $key = $this->_lcfirst(substr($method,3));
                return $this->hasData($key);
        }
        switch (substr($method, 0, 5)) {
            case 'unset' :
                $key = $this->_lcfirst(substr($method,5));
                $result = $this->unsetData($key);
                return $result;
            case 'empty' :
                $key = $this->_lcfirst(substr($method,5));
            	return empty($this->$key);
        }
		$exception = sprintf('Invalid method %s::%s(%s)', get_class($this), $method, print_r($args,1));
    	throw new Exception($exception);
    }

    /**
     * @param string $key
     * @return mixed
     */
    public function getData($key = null)
    {
    	if(is_null($key)) {
    		return $this->_data;
    	}
    	if(!array_key_exists($key, $this->_data)) {
			$exception = sprintf('Invalid key "%s" in %s::getData()', $key, get_class($this));
        	throw new Exception($exception);
    	}
		return $this->_data[$key];
    }

	/**
	 * @param string $key
	 * @param mixed $value
	 */
    public function setData($key, $value)
    {
    	if(!array_key_exists($key, $this->_data)) {
			$exception = sprintf('Invalid key "%s" in %s::setData()', $key, get_class($this));
        	throw new Exception($exception);
    	}
		$this->_data[$key] = $value;
    }

    /**
     * @param string $key
     */
    public function unsetData($key)
    {
    	if(!array_key_exists($key, $this->_data)) {
			$exception = sprintf('Invalid key "%s" in %s::unsetData()', $key, get_class($this));
        	throw new Exception($exception);
    	}
		$this->_data[$key] = null;
    }

    /**
     * @param string $key
     * @return boolean
     */
    public function hasData($key)
    {
    	if(!array_key_exists($key, $this->_data)) {
			$exception = sprintf('Invalid key "%s" in %s::hasData()', $key, get_class($this));
        	throw new Exception($exception);
    	}
		return isset($this->_data[$key]);
    }

    /**
     * @param WSB_Model_Gateway $gateway
     */
    public function setGateway(WSB_Model_Gateway $gateway)
    {
    	$this->_gateway = $gateway;
    }

    /**
     * @return WSB_Model_Gateway
     */
    public function getGateway()
    {
    	return $this->_gateway;
    }

	/**
	 * @param string $name
	 * @param mixed $value
	 */
	protected function __set($name, $value)
	{
		if(!array_key_exists($name, $this->_data)) {
			$exception = sprintf('Property "%s" not defined in class "%s"', $name, get_class($this));
			throw new Exception($exception);
		}
		$this->_data[$name] = $value;
	}

	/**
	 * @param string $name
	 * @return mixed
	 */
	protected function __get($name)
	{
		if(!array_key_exists($name, $this->_data)) {
			$exception = sprintf('Property "%s" not defined in class "%s"', $name, get_class($this));
			throw new Exception($exception);
		}
		return $this->_data[$name];
	}

	/**
	 * @param string $name
	 * @return boolean
	 */
	protected function __isset($name)
	{
		return isset($this->_data[$name]);
	}

	/**
	 * @param string $name
	 */
	protected function __unset($name)
	{
		if(isset($this->$name)) {
			$this->_data[$name] = null;
		}
	}

	/**
	 * @return array
	 */
	public function toArray()
	{
		return $this->_data;
	}

	/**
	 * @todo
	 * @return string
	 */
	public function toJson()
	{
		return Zend_Json::encode($this->toArray());
	}

	/**
	 * @todo
	 * @return string
	 */
	public function toXml()
	{
	}

	/**
	 * @param string $str
	 * @return string
	 */
	private function _lcfirst($str) {
		$str[0] = strtolower($str[0]);
		return $str;
	}

}
